import {
  Viewer,
  Entity,
  SampledPositionProperty,
  ReferenceFrame,
  Color,
  TimeIntervalCollection,
  TimeInterval,
  JulianDate,
  Cartesian3,
  PolylineGlowMaterialProperty,
  ArcType,
  LagrangePolynomialApproximation,
  ClockRange,
  Timeline,
  Math,
  CustomDataSource,
  HeightReference,
  DistanceDisplayCondition,
  CallbackProperty,
  Cartographic,
  Matrix4,
  PrimitiveCollection,
  CylinderGeometry,
  PerInstanceColorAppearance,
  Primitive,
  GeometryInstance,
  ColorGeometryInstanceAttribute,
  Material,
  ConstantProperty,
  Rectangle,
  ClassificationType,
  ShadowMode,
  UrlTemplateImageryProvider,
  ImageryLayer
} from 'cesium';

interface SatelliteOptions {
  referenceFrame?: ReferenceFrame;
  pointColor?: Color;
  pathColor?: Color;
  pointPixelSize?: number;
  pathWidth?: number;
  ImageUrl?: string,
  billWidth?: number,
  billHeight?: number,
  positions?: Position[];
  cyTopRadius?: number;
  cyBottomRadius?: number;
  cyColor?: string;
  cyShow?: boolean;
  cyDefaultLength?: number;
  times?: TimeString[];
}

interface SatelliteRecord {
  entity: Entity;
  positionProperty: Position[];
  referenceFrame: ReferenceFrame;
  timesProperty: TimeString[],
  configProperty: SatelliteOptions
}
interface GlobalConfig {
  clock?: null | Timeline,
  times?: string[],
  DataSource: CustomDataSource | null
}

interface timeConfigType {
  startTime: Date | string,
  stopTime: Date | string,
  currentTime?: Date | string,
  shouldAnimate?: boolean,
  multiplier?: number,
  clockRange?: ClockRange
}
type GetEntityType = { entity: undefined | Entity, line: undefined | Entity };
type Position = [number, number, number]; // [x/y/lon, y/z/lat, z/alt]
type TimeString = string; // ISO 8601 format
type ReferenceFrameType = "FIXED" | "INERTIAL"
export class useSatelliteManager {
  viewer: Viewer;
  /**
   * 保存绘制的卫星对象
   */
  satellites: Map<string, SatelliteRecord>;
  _currentReferenceFrame: ReferenceFrame;
  /**
   * 属于该类的唯一标识
   */
  #niqueSign: string = "_Satellite"
  /**
   * 卫星移动对象的名称后缀
   */
  #SATELLITE_SUDDIX: string = `_PATH${this.#niqueSign}`
  /**
   * 卫星轨迹线对象的名称后缀
   */
  #LINE_SUDDIX: string = `_LINE${this.#niqueSign}`
  /**
   * 全局配置
   */
  #globalConfig: GlobalConfig = {
    DataSource: null,
    clock: null,
    times: []
  }

  /**
   * 
   */
  #viewerDataSource: CustomDataSource | Viewer;

  cylinderPrimitives = new PrimitiveCollection();

  constructor(viewer: Viewer, config?: GlobalConfig) {

    this.viewer = viewer;
    this.viewer.scene.primitives.add(this.cylinderPrimitives);

    this.satellites = new Map();
    this._currentReferenceFrame = ReferenceFrame.FIXED;

    this.#globalConfig = {
      ...this.#globalConfig,
      ...config
    }

    this.#viewerDataSource = config?.DataSource || viewer;
  }

  addSatellite(id: string, options: SatelliteOptions = {}): Entity {
    const defaults: Required<SatelliteOptions> = {
      referenceFrame: this._currentReferenceFrame,
      pointColor: Color.YELLOW,
      pathColor: Color.WHITE,
      pointPixelSize: 6,
      pathWidth: 2,
      ImageUrl: "/static/images/wx.png",
      billWidth: 80,
      billHeight: 80,
      positions: [],
      cyTopRadius: 10,
      cyBottomRadius: 10000,
      cyColor: "red",
      cyShow: false,
      cyDefaultLength: 1000.0,
      times: this.#globalConfig.times || []
    };

    const config = { ...defaults, ...options };

    const positionProperty = new SampledPositionProperty(config.referenceFrame);

    const that = this;
    this.removeSatelliteById(id);
    const satellite = this.#viewerDataSource.entities.add({
      id: `${id}${this.#SATELLITE_SUDDIX}`,
      availability: new TimeIntervalCollection(),
      position: positionProperty,
      point: {
        pixelSize: config.pointPixelSize,
        color: config.pointColor,
        outlineColor: Color.BLACK,
        outlineWidth: 1
      },
      billboard: {
        image: config.ImageUrl,
        scale: 1,
        width: config.billWidth,
        height: config.billHeight,
      },
      path: {
        resolution: 60,
        leadTime: 1,
        trailTime: 1,
        material: new PolylineGlowMaterialProperty({
          glowPower: 1,
          color: config.pathColor
        }),
        width: config.pathWidth
      },
      cylinder: {
        topRadius: config.cyTopRadius,
        bottomRadius: config.cyBottomRadius,
        length: new CallbackProperty(function (time: JulianDate) {
          const position = positionProperty.getValue(time);
          if (!position) return config.cyDefaultLength; // 默认高度
          const cartographic = Cartographic.fromCartesian(position);
          let height = cartographic.height;

          that.createHoleEntity({ ...cartographic, id: `${id}${that.#SATELLITE_SUDDIX}` })

          return height; // 自定义高度计算
        }, false),
        fill: true,
        heightReference: HeightReference.CLAMP_TO_GROUND,
        slices: 64, // 提高曲面质量
        show: config.cyShow,
        material: Color.fromCssColorString(config.cyColor).withAlpha(0.3)
      },
    });

    this.satellites.set(id, {
      entity: satellite,
      configProperty: config,
      positionProperty: options.positions || [],
      referenceFrame: config.referenceFrame,
      timesProperty: options.times || []
    });

    this.addSampledPositions(id, config.positions, config.times)
    return satellite;
  }
  addSampledPositions(id: string, positions: Position[], times: TimeString[]): void | boolean {
    if (!positions.length) {
      return false;
    }
    let satellite = this.satellites.get(id);

    const { entity, line } = this.getSatelliteEntity(id);

    let _times = times || this.#globalConfig.times
    let _line = line;
    // 检查时间是否合理
    if (!this._validateTimeVaildByString(_times)) {
      throw new Error('请输入合法的时间');
    }

    if (!satellite) throw new Error('Satellite not found');

    const availabilityIntervals: TimeInterval[] = [];

    let positionProperty: SampledPositionProperty | undefined = entity && (entity.position as SampledPositionProperty);

    if (!(positionProperty instanceof SampledPositionProperty)) positionProperty = new SampledPositionProperty(this._currentReferenceFrame);

    let cartesianList: Cartesian3[] = [];
    let startTime: JulianDate | null = JulianDate.fromDate(new Date(_times[0]));
    let stopTime: JulianDate | null = JulianDate.fromDate(new Date(_times[0]));
    positions.forEach((pos, index) => {
      stopTime = JulianDate.fromDate(new Date(_times[index]));

      let cartesian: Cartesian3;

      if (satellite.referenceFrame === ReferenceFrame.FIXED) {
        cartesian = Cartesian3.fromDegrees(pos[0], pos[1], pos[2]);
      } else {
        cartesian = new Cartesian3(pos[0], pos[1], pos[2]);
      }

      positionProperty.addSample(stopTime, cartesian);
      cartesianList.push(cartesian)
    });

    positionProperty.setInterpolationOptions({
      interpolationDegree: 0.0001,
      interpolationAlgorithm: LagrangePolynomialApproximation
    })
    availabilityIntervals.push(new TimeInterval({
      start: startTime,
      stop: stopTime
    }));

    if (_line) this.removeLineById(id)
    _line = this.#viewerDataSource.entities.add({
      id: `${id}${this.#LINE_SUDDIX}`,
      name: `${id}_name`,
      polyline: {
        positions: cartesianList,
        material: new PolylineGlowMaterialProperty({
          glowPower: 0.2,
          color: Color.fromCssColorString("rgba(0, 255, 255, 0.8)"),
          taperPower: 1
        }),
        width: 5,
        arcType: ArcType.GEODESIC,
        clampToGround: false
      }
    })

    satellite.entity.availability = new TimeIntervalCollection(availabilityIntervals);

    if (satellite) {
      this.satellites.set(id, {
        ...satellite,
        timesProperty: times || []
      })
    }

    if (entity) {
      entity.availability = new TimeIntervalCollection(availabilityIntervals);
      entity.position = positionProperty;
    }

  }
  /**
   * 绘制圆柱体 - 暂时用不上
   * @param position 
   */
  addCylinderEntity(position: Cartesian3) {
    const cartographic = Cartographic.fromCartesian(position);
    const height = cartographic.height;
    // 创建空心圆柱几何体
    const cylinderGeometry = new CylinderGeometry({
      length: height,
      topRadius: 10,
      bottomRadius: 8000,
      vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT
    });

    // 创建仅显示线框的外观
    const appearance = new PerInstanceColorAppearance({
      flat: true,
      renderState: {
        depthTest: { enabled: true },
        lineWidth: 2 // 线宽
      }
    });

    // 创建Primitive
    const primitive = new Primitive({
      geometryInstances: new GeometryInstance({
        geometry: cylinderGeometry,
        attributes: {
          color: ColorGeometryInstanceAttribute.fromColor(Color.RED.withAlpha(0.8))
        }
      }),
      appearance: appearance,
      asynchronous: false
    });

    // 计算偏移位置（使顶部在卫星位置）
    const offsetPosition = Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      cartographic.height + height / 2
    );

    primitive.modelMatrix = Matrix4.fromTranslation(offsetPosition);
    this.cylinderPrimitives.add(primitive);
  }

  /**
   * 遮罩层
   */
  maskRectangle: Entity | Primitive | null = null;
  createMaskLayer(): boolean {
    // 创建遮罩层
    const maskMaterial = new Material({
      fabric: {
        type: 'Color',
        uniforms: {
          color: new Color(0.0, 0.0, 0.0, 0.7)
        }
      }
    });
    let that = this;

    if (that.getSatelliteEntityById(`mask${that.#niqueSign}`)) {
      return false;
    }
    this.imageryLayer = this.viewer.imageryLayers.addImageryProvider(
      new UrlTemplateImageryProvider({
        url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
      })
    );

    that.maskRectangle = that.#viewerDataSource.entities.add({
      id: `mask${that.#niqueSign}`,
      rectangle: {
        show: true,
        coordinates: Rectangle.fromDegrees(-180, -90, 180, 90),
        material: Color.fromCssColorString("#a09").withAlpha(0.7),
        classificationType: ClassificationType.BOTH,
        zIndex: 5 // 确保在孔洞下方
      }
    });

    return true;
  }
  imageryLayer: ImageryLayer | null = null;
  createHoleEntity(cartesian): Entity | boolean {
    let { longitude, latitude, height, id } = cartesian;
    const position = Cartesian3.fromDegrees(
      longitude,  // 经度(度)
      latitude,   // 纬度(度)
      height      // 高度(米)，可选，默认为0
    );

    let _id = `${id}_hole`;
    const holeSize = 0.9; // 约100km

    let entity = this.getSatelliteEntityById(_id);
    if (entity) {
      let { x, y, z } = this.degreesToCartesian(longitude, latitude, height);
      let { longitude: lon, latitude: lat } = this.cartesianToDegrees(new Cartesian3(x + holeSize, y + holeSize, z));
      const holeRectangle = Rectangle.fromDegrees(
        longitude,
        latitude,
        lon,
        lat
      );

      if (entity.rectangle && entity.rectangle.coordinates && entity.rectangle.coordinates instanceof ConstantProperty) {
        entity.rectangle.coordinates.setValue(holeRectangle);
      } else {
        entity.rectangle && (entity.rectangle.coordinates = new ConstantProperty(holeRectangle));
      }

      return false;
    }
    // 创建孔洞区域
    const holeRectangle = Rectangle.fromDegrees(
      longitude - holeSize / 2,
      latitude - holeSize / 2,
      longitude + holeSize / 2,
      latitude + holeSize / 2
    );

    // 添加孔洞实体
    return this.#viewerDataSource.entities.add({
      id: _id,
      rectangle: {
        coordinates: holeRectangle,
        material: new Color(0.0, 0.0, 0.0, 0.3),
        outline: true,
        stRotation: 0,
        outlineColor: Color.YELLOW,
        outlineWidth: 2,
        shadows: ShadowMode.DISABLED, // 禁用阴影
        classificationType: ClassificationType.BOTH,
        zIndex: 30
      }
    });
  }
  /**
   * 修改卫星探测光波状态
   * @param id 
   * @param status 
   */
  setSatelliteDetection(id: string, status: boolean): boolean {
    let { entity } = this.getSatelliteEntity(id);

    let sate = this.satellites.get(id);

    if (!entity || !entity.cylinder) return false;
    entity.cylinder.show = new ConstantProperty(status || false)
    if (sate && sate.entity && sate.entity.cylinder) sate.entity.cylinder.show = new ConstantProperty(status || false)
    return true;
  }
  /**
   * 修改坐标 地固系 - 惯性系
   * @param frame "FIXED" | "INERTIAL"
   */
  setReferenceFrame(frame: ReferenceFrameType): void {
    let _frame = frame == "FIXED" ? ReferenceFrame.FIXED : ReferenceFrame.INERTIAL;
    this._currentReferenceFrame = _frame;
    this.AgainDrawEntity({ referenceFrame: _frame })
  }
  /**
   * 设置卫星中的时间
   * @param times 
   */
  setSatelliteTimes(times: string[] | undefined) {
    if (!times || !this._validateTimeVaildByString(times)) {
      throw new Error("请提供有效时间")
    }
    this.#globalConfig.times = times;
    this.AgainDrawEntity({
      timesProperty: times
    })
  }
  /**
   * 当更改参数时，重新绘制
   * @param params 
   */
  AgainDrawEntity(params: Partial<SatelliteRecord>) {
    let sate = this.satellites.entries();
    let sateObj = sate.next();
    while (!sateObj.done) {
      let id = sateObj.value[0];
      let obj = sateObj.value[1];
      this.addSatellite(id, {
        ...obj.configProperty,
        ...params
      })
      sateObj = sate.next();
    }
  }
  /**
   * 更新时钟
   * @param timeConfig 
   */
  _updateClockRange(timeConfig: timeConfigType): void {

    let { shouldAnimate, startTime, multiplier, currentTime, stopTime, clockRange } = {
      shouldAnimate: false,
      currentTime: null,
      multiplier: 1,
      clockRange: ClockRange.CLAMPED,
      ...timeConfig
    };

    let _clock = this.viewer.clock;

    if (!_clock) {
      throw new Error("未找到clock对象")
    }

    if (!startTime || !stopTime) {

      let _times = this.#globalConfig.times;
      if (_times && _times.length > 1) {
        _clock.startTime = JulianDate.fromDate(new Date(_times[0]));
        _clock.currentTime = JulianDate.fromDate(new Date(_times[0]));
        _clock.stopTime = JulianDate.fromDate(new Date(_times[_times.length - 1]));
      }

    } else {
      _clock.startTime = JulianDate.fromDate(new Date(startTime));
      _clock.stopTime = JulianDate.fromDate(new Date(stopTime));
      _clock.currentTime = JulianDate.fromDate(new Date(currentTime || stopTime));
    }

    _clock.clockRange = clockRange;
    _clock.shouldAnimate = shouldAnimate;
    _clock.multiplier = multiplier;

    //  * JulianDate.lessThan(start, this.viewer.clock.startTime)
    //  * JulianDate.greaterThan(stop, this.viewer.clock.stopTime)
  }
  /**
   * 通过一组字符串数组查看是否能转成合法时间
   * @param timeStr timeStr[]
   */
  _validateTimeVaildByString(timeStr: TimeString[]): Boolean {

    for (let i = 0; i < timeStr.length; i++) {
      const date = new Date(timeStr[i]);
      if (isNaN(date.getTime())) {
        console.error(`${timeStr[i]}不合法`);
        return false;
      }
    }
    return true;
  }
  /**
   * 经纬度转大地坐标（笛卡尔积）
   * @param longitude 
   * @param latitude 
   * @param height 
   * @returns 
   */
  degreesToCartesian(longitude: number, latitude: number, height = 0): Cartesian3 {
    return Cartesian3.fromDegrees(longitude, latitude, height);
  }
  /**
   * 大地坐标转经纬度
   * @param cartesian 
   * @returns 
   */
  cartesianToDegrees(cartesian: Cartesian3): Cartographic {
    const cartographic = Cartographic.fromCartesian(cartesian);
    return new Cartographic(Math.toDegrees(cartographic.longitude), Math.toDegrees(cartographic.latitude), cartographic.height)
  }
  /**
   * 通过id查找指定的卫星对象
   * @param id 
   * @returns 
   */
  getSatelliteEntity(id: string): GetEntityType {
    if (!id) return { entity: undefined, line: undefined };
    return {
      entity: this.getSatelliteEntityById(`${id}${this.#SATELLITE_SUDDIX}`),
      line: this.getSatelliteEntityById(`${id}${this.#LINE_SUDDIX}`)
    }
  }
  /**
   * 根据id查找元素
   * @param id 
   * @returns 
   */
  getSatelliteEntityById(id: string): Entity | undefined {
    return this.viewer.entities.getById(id)
  }
  /**
   * 更改id删除线
   * @param id 
   * @returns 
   */
  removeLineById(id: string): boolean {
    return this.removeEntity(`${id}${this.#LINE_SUDDIX}`)
  }
  /**
   * 更改id删除移动对象
   * @param id 
   * @returns 
   */
  removeSatelliteById(id: string): boolean {
    return this.removeEntity(`${id}${this.#SATELLITE_SUDDIX}`)
  }
  /**
   * 删除对象
   * @param content 可以是id或实体对象
   * @returns boolean
   */
  removeEntity(content: string | Entity): boolean {
    if (!content) return false;
    if (content instanceof Entity) {
      return this.viewer.entities.remove(content);
    } else {
      return this.viewer.entities.removeById(content);
    }
  }
  /**
   * 根据id删除卫星对象
   * @param id 
   */
  removeSatellite(id: string): void {
    const { entity, line } = this.getSatelliteEntity(id);
    entity && this.removeEntity(entity);
    line && this.removeEntity(line)
    this.satellites.delete(id);
  }
  /**
   * 销毁实例
   */
  destroySatellite() {
    let that = this;
    let sateVal = this.satellites.keys();
    Array.from(sateVal).forEach((item: string) => {
      that.removeSatellite(item)
    })
    this.satellites.clear()
  }
}
